import {
  Component,
  OnInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  OnChanges,
  OnDestroy,
  SimpleChanges,
  ChangeDetectorRef,
  TemplateRef,
  TrackByFunction,
  ViewChild,
} from '@angular/core';
import { AnimationEvent } from '@angular/animations';
import { SelectionModel } from '@angular/cdk/collections';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Subject, Subscription } from 'rxjs';
import { takeUntil, debounceTime } from 'rxjs/operators';
import { map as _map, filter as _filter, forEach as _forEach, some as _some, isEqual as _isEqual } from 'lodash';
import { flatMap as lvFlatMap, InputBoolean, InputNumber, result as lvResult } from '../core/utils/api';
import { MotionDurationTime } from '../core/animation/api';
import { LvSelectPCB, LvSelectSM } from '../core/types';
import { LvConfig } from '../core/config';
import {
  EventName,
  TreeCallback,
  TreeFilterConfig as FilterConfig,
  TreeNode,
  TreeNodeKey,
  TreeDragDrop,
} from './interface';
import { TreeService } from './tree.service';
import { TreeDataSource } from './tree-data-source';
import { getFlatTreeMap, flattenTreeData } from './utils';
import { treeCollapseMotion } from './tree-motion';

@Component({
  selector: 'lv-tree',
  templateUrl: './tree.component.html',
  providers: [TreeService],
  animations: [treeCollapseMotion],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    class: 'lv-tree',
    '[class.lv-tree-line]': 'lvShowLine',
    '[class.lv-tree-checkbox]': 'lvShowCheckbox',
    '[@.disabled]': '!viewInitCompleted',
  },
})
export class TreeComponent implements OnInit, OnChanges, OnDestroy {
  // 输入输出属性
  @Input() lvData: TreeNode[]; // 源数据
  @Input() lvSelection: TreeNode[]; // 选中项
  @Input() lvBeforeDrop: TreeCallback; // NEED_DO

  @Input() lvSelectionMode: LvSelectSM; // 选择模式
  @Input() lvBeforeSelected: TreeCallback; // 选中前执行的方法
  @Input() lvBeforeExpanded: TreeCallback; // NEED_DO:展开前执行的方法，返回false
  @Input() lvHighlightContent: string = '';
  @Input() @InputBoolean() lvAsync: boolean; // 是否为异步
  @Output() lvClick = new EventEmitter<any>(); // 节点单击回调
  @Output() lvCheck = new EventEmitter<any>(); // 节点check回调
  @Input() @InputBoolean() lvDraggable = false; // 同层级的节点是否支持拖拽交换位置，虚拟滚动模式下不支持

  @Output() lvSelect = new EventEmitter<any>(); // 节点check或单击回调
  @Input() @InputNumber() lvVirtualItemSize = 32; // 每行高度
  @Output() lvDblClick = new EventEmitter<any>(); // 节点双击回调
  @Output() lvDragDrop = new EventEmitter<any>(); // 节点拖拽交换位置后的回调
  @Output() lvScrollEnd = new EventEmitter<any>(); // 虚拟滚动到底部时回调事件
  @Input() @InputBoolean() lvShowCheckbox: boolean; // 是否显示checkbox

  // == NEED_DO: virtual scroll
  @Input() @InputBoolean() lvVirtualScroll = false; // 虚拟滚动
  @Input() @InputNumber() lvVirtualMaxBuffer = 200; // 渲染新项时最大缓冲高度
  @Input() @InputNumber() lvVirtualMinBuffer = 100; // 视图之外渲染的最小缓冲区数，若缓存区数量低于此数量将渲染新项

  @Input() lvSourceData: TreeDataSource<TreeNode[]>; // 超级data
  @Input() lvCompareWith: string | ((o: any) => any); // data被选中的比较方式，默认地址比较
  @Input() lvSelectedValueMode: LvSelectPCB = 'both';
  @Input() @InputBoolean() lvShowLine: boolean = true; // 是否展示连接线
  @Input() @InputBoolean() lvShowContentIcon: boolean; // 是否显示内容前icon
  @Output() lvAnimationDone = new EventEmitter<any>(); // 树节点展开完成
  @Input() @InputBoolean() lvSelectionAssociate = true; // 选择关联，选中是否关联父子选中
  @Output() lvExpandedChange = new EventEmitter<any>(); // 节点展开变化回调

  @Input() @InputBoolean() lvSelectedByCheckbox = false; // 是否只能checkbox选中
  @Output() lvSelectionChange = new EventEmitter<any>(); // 选中项变化回调
  @Output() lvScrollingChange = new EventEmitter<any>(); // 虚拟滚动中回调事件
  @Input() @InputBoolean() lvKeepSelectedInSingle = false; // 单选模式下，选中再次点击不会被取消
  @Input() @InputBoolean() lvKeepExpanded: boolean = true; // 过滤条件清除之后是否保留展开能力
  @Output() lvRenderDataChange = new EventEmitter<void>(); // 内部API，在平铺后的展示数据列表变更时进行回调，用于treeSelect更新面板位置
  @Input() lvNodeTemplate: TemplateRef<{ $implicit: TreeNode }>;
  @Input() lvScroll: { y?: string | null } = {
    y: '500px',
  };

  @Input() lvShowExpandedIcon: boolean | { expandIcon: string; collapseIcon: string } = true; // 展开折叠图标
  @ViewChild('scrollComponent', { static: false }) _scrollViewport: CdkVirtualScrollViewport;
  // NEED_DO @Input() @InputBoolean() lvHideUnMatched;'
  @Input() @InputBoolean() lvBlockNode: boolean =
    LvConfig.treeOptions.lvBlockNode !== undefined ? LvConfig.treeOptions.lvBlockNode : false;

  renderDataMap: any; // map对象any

  // 外部可访问变量
  renderData: any[] = []; // 可视数据（源数据过滤后的data）
  viewInitCompleted: boolean;
  flattenData: TreeNode[] = []; // 平铺数据，用于展示，过滤hidden后的平铺数据，外部有使用此数据，故变量名不变更
  halfSelection: TreeNode[] = []; // 半选
  refreshView$ = new Subject<void>();
  _dataSource: TreeDataSource<TreeNode>;
  selectionChange$ = new Subject<void>();
  // 内部变量
  private _destroy$ = new Subject<void>();

  private _renderSub = Subscription.EMPTY;

  private _selection: SelectionModel<any>;
  private _selectionData: TreeNode[] = [];
  private _unfoldSelectionData: Map<any, TreeNode>; // 平铺的selectionData
  constructor(
    private treeService: TreeService,
    private _cdr: ChangeDetectorRef,
  ) {
    this.treeService.setRootComponent(this);
    this.setSelection();
  }

  get multiple(): boolean {
    return this.treeService.isMultipleMode();
  }

  @Input() lvTrackBy: TrackByFunction<any> = (_: number, node: TreeNode): any => node.key || node._lv_id; // 内容列表trackBy

  // 获取全部选中项-全部
  getSelection(): any[] {
    return this._selection.selected;
  }

  /**
   * 获取data主键，默认为对象地址
   * @param source 源数据
   */
  result(source: any): any {
    return lvResult(source, this.lvCompareWith);
  }

  ngOnInit(): void {
    this._subscribeRefreshView();
    this.lvBeforeSelected;
  }

  ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
  }

  onAnimationDone(event: AnimationEvent): void {
    this.lvAnimationDone.emit(event);
  }

  bulkResult(source: any[]): any {
    return _map(source, (item): any => this.result(item));
  }

  // 选中
  selectSelection<T>(value: T): void {
    this.refreshSelectionMap([value]);
    this._selection.select(value);
  }

  // 反选
  deselectSelection<T>(value: T): void {
    this.refreshSelectionMap([value]);
    this._selection.deselect(value);
  }

  /**
   * 选择
   */
  getParentSelectionData(source: TreeNode): TreeNode {
    return this._unfoldSelectionData.get(this.result(source))?.parent;
  }

  // 是否选中
  isSelected<T>(value: T): boolean {
    return _some(this.getSelection(), (o): boolean => this.result(value) === this.result(o));
  }

  // 清除所有-全部
  clearSelection(emit: boolean = true): void {
    this._selection?.clear();
    if (emit) {
      this.updateSelection();
    }
  }

  // 清除所有-当前渲染 NEED_DO:按renderData计算有些不合理
  clearRenderSelection(emit: boolean = true): void {
    this.deleteSelection(this.renderData);
    if (emit) {
      this.updateSelection();
    }
  }

  // 是否半选
  isHalfSelected<T>(value: T): boolean {
    return _some(this.halfSelection, (h): boolean => this.result(value) === this.result(h));
  }

  // 切换
  toggleSelectionByStatus<T>(value: T, status: boolean): void {
    status ? this.selectSelection(value) : this.deselectSelection(value);
  }

  // 订阅更新视图的流
  _subscribeRefreshView(): void {
    this.refreshView$.pipe(debounceTime(1), takeUntil(this._destroy$)).subscribe((): void => this._cdr.markForCheck());
  }

  // 获取当前渲染项选中项-当前渲染
  getRenderSelection(): any {
    const selected = this.getSelection();
    return _filter(selected, (item): any => this.renderDataMap.has(this.result(item)));
  }

  hasUnselectedNode(parentNode): TreeNode {
    // 是否有未选中的子项，有就不选中
    return parentNode.children.some((item): boolean => !item.disabled && !this.isSelected(item));
  }

  _reFilter(): void {
    const map = this.treeService.getFilterState();
    this._dataSource.keepExpanded = this.lvKeepExpanded;
    this._dataSource.setFilterState(map);
  }

  // 批量选中项-全部
  bulkSelection<T>(values: T[], emit: boolean = true): void {
    this.refreshSelectionMap(values);
    this._selection.select(...values);
    if (emit) {
      this.updateSelection();
    }
  }

  resultToDataMap(source: any[]): Map<any, any> {
    const map = new Map();
    _map(source, (item): void => {
      map.set(this.result(item), item);
    });
    return map;
  }

  // 批量删除项
  deleteSelection<T>(values: T[], emit: boolean = true): void {
    this.refreshSelectionMap(values);
    this._selection.deselect(...values);
    if (emit) {
      this.updateSelection();
    }
  }

  expandedChange(e): void {
    this.handleFlattenNodes(
      this.renderData,
      this.treeService.getExpandedNodeList().map((v): any | string => v.key),
    );
    this.lvExpandedChange.emit(e);
  }

  // 切换选中项并触发change
  _toggleSelection(source: any, selected: boolean, parentNode: boolean = false): void {
    if (source.disabled && !parentNode) {
      return;
    }
    this.toggleSelectionByStatus(source, selected);
  }

  /**
   * 初始化选中项
   */
  setSelection(): void {
    this.clearSelection(false);
    this._setSelectionData(this.lvSelection);
    this._selection = new SelectionModel(this.multiple, this._selectionData);
    this.selectionChange$.next();
  }

  /**
   * 初始化半选中项
   * 半选状态以属性配置，需先找到配置了半选状态的node
   */
  setHalfSelection(data: TreeNode[]): void {
    for (const node of data) {
      if (node.indeterminate) {
        this.halfSelection.push(node);
      }

      if (node.children) {
        this.setHalfSelection(node.children);
      }
    }
  }

  // filter
  filter(source: FilterConfig): void {
    const _source = {
      ...{ filterMode: 'startsWith', caseSensitive: false },
      ...source,
    } as FilterConfig;
    this.treeService.setFilterState(_source);
    this._reFilter();
  }

  /**
   * 行选中有联动关系时（树表），行选中影响父子行
   * @param source 选中项
   */
  toggleGenerationSelection(source: any): void {
    if (!this.multiple || !this.lvSelectionAssociate || source.disabled) {
      return;
    }

    this._toggleDescendantsSelection(source);
    this._toggleAncestorSelection(source);
  }

  /**
   * 手动清除filter
   * @param key 不传代表清除所有，传代表清除指定key的排序,支持批量清除
   */
  removeFilter(key?: string | string[]): void {
    if (!key) {
      this.treeService.clearFilterState();
    } else {
      const keyArr = typeof key === 'string' ? [key] : key;
      keyArr.forEach((item): void => this.treeService.removeFilterState(item));
    }
    this._reFilter();
  }

  /**
   * 订阅virtual scroll 的scroll事件
   * 记录滚动到底部时，触发回调
   * NEED_DO: 追加的数据，显示错位
   */
  scrolledIndexChange(e): void {
    const view = this._scrollViewport;
    const { end } = view.getRenderedRange();
    const total = view.getDataLength();

    if (!total) {
      return;
    }

    this.lvScrollingChange.emit({ source: view, index: e });
    if (end === total) {
      this.lvScrollEnd.emit();
    }
  }

  // 切换选择项
  toggleSelection<T>(value: T): void {
    if (this.lvSelectionMode === 'single' && this.lvKeepSelectedInSingle && this._selection.isSelected(value)) {
      return;
    }
    this.refreshSelectionMap([value]);
    this._selection.toggle(value);

    // 祖先后代
    this.toggleGenerationSelection(value);

    // 更新selection
    this.updateSelection();
  }

  // renderData 转换为map对象
  getRenderDataMap(data: TreeNode[], target?: any): Map<any, TreeNode> {
    let map = new Map();
    if (target) {
      map = target;
    }
    _forEach(data, (item): void => {
      map.set(this.result(item), item);
      if (item.children?.length) {
        this.getRenderDataMap(item.children, map);
      }
    });
    return map;
  }

  // 刷新selectionModel,记录的selection地址变更 - NEED_DO： 优化
  refreshSelectionMap<T>(values: T[]): void {
    const selected = this.getSelection();
    _forEach(values, (item): void => {
      const temp = _filter(selected, (o): boolean => this.result(o) === this.result(item));
      if (!temp.length) {
        return;
      }
      this._selection.deselect(temp[0]);
      this._selection.select(item);
    });
  }

  /**
   * 后代联动：父选中（反选），子（除去disabled状态子）全选中（反选）
   */
  _toggleDescendantsSelection(source: any): void {
    if (!source || source.disabled) {
      return;
    }
    const selected = this.isSelected(source);
    _forEach(source.children, (item): void => {
      if (item.disabled || item.hidden) {
        return;
      }
      this._toggleSelection(item, selected);
      this._toggleDescendantsSelection(item);
    });
  }

  handleFlattenNodes(data: TreeNode[], expandKeys: TreeNodeKey[] | true = []): void {
    this.flattenData = flattenTreeData(data, expandKeys)
      .map((item): TreeNode => item.data)
      .filter((item): boolean => !item.hidden);

    setTimeout((): void => {
      this.viewInitCompleted = true;
      this._cdr.markForCheck();
    }, MotionDurationTime.MD);
    this.lvRenderDataChange.emit();
  }

  refreshSelectionModule(): any {
    const { data } = this._dataSource;
    const dataMap = lvFlatMap(data, this.lvCompareWith, true);
    const selected = this.getSelection();
    const usefulSelected = _filter(selected, (item): boolean => {
      const key = this.result(item);
      return dataMap.has(key);
    });
    this.clearSelection(false);
    this.bulkSelection(usefulSelected, false);
    return usefulSelected;
  }

  emitEvent(source: any): void {
    const data = { node: source.node, event: source.event };
    switch (source.type) {
      case EventName.CLICK:
        this.lvClick.emit(data);
        this.lvSelect.emit(data);
        break;
      case EventName.DBLCLICK:
        this.lvDblClick.emit(data);
        break;
      case EventName.CHECK:
        this.lvCheck.emit(data);
        this.lvSelect.emit(data);
        break;
      default:
        break;
    }
  }

  /**
   * 祖先联动：子切换，父判断是否子都全选（反选）改变选中状态
   */
  _toggleAncestorSelection(source: any): void {
    if (!source || source.disabled) {
      return;
    }
    const parentRow = this.getParentSelectionData(source);

    if (!parentRow || parentRow.disabled) {
      return;
    }
    // 是否有未选中的子项，有就不选中
    const unselected = parentRow.children.some(
      (item): boolean => !item.hidden && !item.disabled && !this.isSelected(item),
    );
    this._toggleSelection(parentRow, !unselected, true);
    this._toggleAncestorSelection(parentRow);
  }

  findAncestorSelection(item: TreeNode, cacheSelection: any[]): void {
    const parentNode = item.parent;
    if (!parentNode) {
      return;
    }
    const cacheParentNode = cacheSelection.find((o): boolean => this.result(o) === this.result(parentNode));
    if (!cacheParentNode) {
      if (!this.hasUnselectedNode(parentNode)) {
        cacheSelection.push(parentNode);
        this._selection.select(parentNode);
        this.findAncestorSelection(parentNode, cacheSelection);
      }
    }
  }

  updateSelection(): void {
    if (!this._selection) {
      return;
    }
    const selection = this.getSelection();
    this._selectionData.length = 0;
    this._selectionData.push(...selection);

    if (!this.lvSelection) {
      this.lvSelection = [];
    }

    this.lvSelection.length = 0;
    // 不改变lvSelection地址，避免lvSelection双向绑定，修改lvSelection地址触发onChanges
    this.lvSelection.push(...this.getActualSelection(this._selectionData));

    this.updateHalfSelection();
    this.lvSelectionChange.emit(this.lvSelection);
  }

  // NEED_DO 目前只支持只有一层节点的树拖拽，故pro-table的多行表头暂不支持拖拽
  drop(event: CdkDragDrop<string[]>): void {
    // 如果parent相同是可以交换位置的，如果不相同则不交换
    if (this.flattenData[event.previousIndex].parent === this.flattenData[event.currentIndex].parent) {
      // 渲染数据(flattenData)和输入源数据(lvData)都需要交互位置
      moveItemInArray(this.flattenData, event.previousIndex, event.currentIndex);
      // NEED_DO 支持多行表头时lvData的previousIndex和currentIndex需要根据元素的id重新获取
      moveItemInArray(this.lvData, event.previousIndex, event.currentIndex);
      this.lvDragDrop.emit({
        renderData: this.flattenData,
        previousIndex: event.previousIndex,
        currentIndex: event.currentIndex,
      } as TreeDragDrop);
    }
  }

  _subscribeRenderChange(dataSource: TreeDataSource<any>): void {
    if (!dataSource) {
      return;
    }

    const dataStream = dataSource.connect();
    this._renderSub.unsubscribe();
    this._renderSub = dataStream.pipe(takeUntil(this._destroy$)).subscribe((res): void => {
      this.renderData = [...res];
      this.renderDataMap = this.getRenderDataMap(this.renderData);
      this.handleFlattenNodes(this.renderData);
      this._unfoldSelectionData = getFlatTreeMap();
      // 更新默认半选selection，数据变更时先拿到传入的半选状态，再拿到正确的select，去计算半选状态
      this.halfSelection.length = 0;
      this.setHalfSelection(this.flattenData);
      this.updateHalfSelection(false);
      this.refreshView$.next();
    });
  }

  /**
   * 获取回显节点
   */
  getActualSelection(selectedNodes): any {
    // 如果父子节点选中不关联，则选中项按实际展示
    if (!this.lvSelectionAssociate) {
      return selectedNodes;
    }

    let cacheSelectedNodes = [...selectedNodes];

    const getParentNode = (array, index = 0): any => {
      let idx = index;
      const isExist = selectedNodes.some((item): any => {
        if (!array[idx].parent) {
          return false;
        }
        return _isEqual(array[idx].parent, item);
      });
      if (isExist) {
        array.splice(idx, 1);
      } else {
        idx++;
      }
      if (array[idx]) {
        getParentNode(array, idx);
      } else {
        return array;
      }
      return undefined;
    };

    const getChildrenNode = (array): void => {
      cacheSelectedNodes = array.filter((item): boolean => !item.children?.length);
    };

    if (this.lvSelectedValueMode === 'parent') {
      getParentNode(cacheSelectedNodes);
    } else if (this.lvSelectedValueMode === 'child') {
      getChildrenNode(cacheSelectedNodes);
    }

    return cacheSelectedNodes;
  }

  /**
   * 更新半选状态
   * 异步场景下，默认配置了半选状态，子选项还未加载，保留该node；子选项加载后，按selection逻辑判断是否半选
   * 新增参数internalChange判断是否为内部selection变更(用于判断配置了半选状态且没有子项的数据是否保留)
   * 外部引起的变更应保留半选状态node，再计算selection产生的半选node，合并得到最终的半选node
   * NEED_DO:操作了选择项后，应按实际操作进行逻辑计算，异步加载了数据后，若新增子项有被选中，应重新传正确的父级半选状态给组件
   */
  updateHalfSelection(internalChange = true): void {
    const cacheSelection = [];

    // 数据变更时，配置了半选状态的数据保留
    if (!internalChange) {
      for (const half of this.halfSelection) {
        cacheSelection.push(half);
      }
    }

    const getParentHalfSelection = (node: TreeNode): void => {
      if (node.parent) {
        // 半选数组中是否已存在
        const isExist = (value: TreeNode): boolean =>
          _some(cacheSelection, (s): boolean => this.result(value) === this.result(s));
        // 更新半选状态的数组
        // 1.半选数组中不存在
        // 2.未被选中
        // 3.父子节点不存在关联关系
        // 4.初始化时父节点不为disabled状态，父节点的半选状态受当前节点影响；父节点为disabled状态，反之亦然。
        // 5.初始化时当前节点不为disabled状态，该节点的父节点和子节点的半选状态受当前节点的影响；当前节点为disabled状态，反之亦然。
        const flag =
          !isExist(node.parent) &&
          !this.isSelected(node.parent) &&
          this.lvSelectionAssociate &&
          !node.parent?.disabled &&
          !node?.disabled;
        if (flag) {
          cacheSelection.push(node.parent);
        }
        getParentHalfSelection(node.parent);
      }
    };

    if (this._selectionData?.length) {
      for (const selection of this._selectionData) {
        getParentHalfSelection(selection);
      }
    }

    this.halfSelection = [...cacheSelection];
  }

  ngOnChanges(changes: SimpleChanges): void {
    const modeChange = changes.lvSelectionMode;
    if (modeChange) {
      this.treeService.setMode(modeChange.currentValue);
    }

    const dataChange = changes.lvData;
    const dataSource = changes.lvDataSource;
    const selectionChange = changes.lvSelection;

    // 初始化dataSource
    if (dataSource) {
      // 原生dataSource用法
      this._dataSource = dataSource.currentValue;
      this._subscribeRenderChange(this._dataSource);
    } else if (dataChange) {
      // 普通data用法 默认配置好dataSource
      const data = dataChange.currentValue;
      // 避免重复初始化
      if (!this._dataSource) {
        this._dataSource = new TreeDataSource(data, this.lvAsync, this.lvCompareWith, this.lvKeepExpanded);
        this._subscribeRenderChange(this._dataSource);
      } else {
        this._dataSource.data = data;
        this._dataSource.keepExpanded = this.lvKeepExpanded;
      }
    }

    // NEED_DO compareWith属性不再使用全局变量，注释
    if (!this._dataSource) {
      return;
    }

    if ((dataSource || dataChange) && this.lvShowLine) {
      setTimeout((): void => this.treeService.setLineChangeStream(this._dataSource.data), 0);
    }

    const flag = dataSource || dataChange || selectionChange || modeChange;
    if (flag) {
      // 更新selection
      this.setSelection();
      this.refreshSelectionModule();
    }

    if (selectionChange) {
      this.updateHalfSelection();
    }
  }

  /**
   * 获取实际的selectionNode
   * @param selectionNode 选中节点
   */
  private _setSelectionData(selectionNode: TreeNode[]): void {
    if (!selectionNode?.length) {
      this._selectionData = [];
      return;
    }

    if (!this.lvSelectionAssociate) {
      this._selectionData = [...this.lvSelection];
      return;
    }

    // NEED_DO child模式，是否需要遍历所有的parent
    const cacheSelection = [];
    switch (this.lvSelectedValueMode) {
      case 'parent':
        this._selectionData = flattenTreeData(this.lvSelection, true).map((item): TreeNode => item.data);
        break;

      case 'child':
        this._selection = new SelectionModel(this.multiple, this.lvSelection);
        setTimeout((): void => {
          this.lvSelection.forEach((item): void => {
            this.findAncestorSelection(item, cacheSelection);
          });
        }, 0);
        this._selectionData = [...this.lvSelection, ...cacheSelection];
        break;

      default:
        this._selectionData = [...this.lvSelection];
        break;
    }
  }
}
